package com.cloudbroker.bcs.platform.dav.middle.model;

import java.io.Serializable;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;

import com.cloudbroker.bcs.common.exception.HSBCSErrInfoException;
import com.cloudbroker.bcs.common.util.DateTimeUtil;
import com.cloudbroker.bcs.common.validatioin.constants.ValidationErrorCodes;
import com.cloudbroker.bcs.platform.dav.common.config.ConfigValues;

public class CompositeSN implements Serializable {
    
    public static final String DEFAULT_TENANT_ID = "0000";
    
    public static final int PART_LEN_TYPE = 2;
    
    public static final int PART_LEN_TENANTID = ConfigValues.TENANT_ID_WIDTH;
    
    public static final int PART_LEN_DATE = 8;
    
    public static final int PART_LEN_SN = 10;
    
    public static final int FULL_LEN = PART_LEN_TYPE + PART_LEN_TENANTID + PART_LEN_DATE + PART_LEN_SN;
    
    public static final String DATE_FORMAT = DateTimeUtil.FORMAT_YYYYMMDD_NO_BREAK;
    
    public static final String SN_FORMAT = "0000000000";
    
    private static final long SN_LIMIT = 9999999999L;
    
    private static final String TYPE_PATTERN_STRING = "([A-Z]{" + PART_LEN_TYPE + "})";
    
    private static final String TENANTID_PATTERN_STRING = "([\\da-zA-Z]{" + PART_LEN_TENANTID + "})";
    
    private static final String DATE_PATTERN_STRING = "([\\d]{" + PART_LEN_DATE + "})";
    
    private static final String SN_PATTERN_STRING = "([\\d]{" + PART_LEN_SN + "})";
    
    private static final String FULL_SN_PATTERN_STRING = TYPE_PATTERN_STRING + TENANTID_PATTERN_STRING
            + DATE_PATTERN_STRING + SN_PATTERN_STRING;
    private static final long serialVersionUID = 2894265997545997814L;

    private static Pattern fullSNPattern = Pattern.compile(FULL_SN_PATTERN_STRING);
    
    private String type;
    
    private String tenantId;
    
    private String dateString;
    
    private Date date;
    
    private String sn;
    
    private String fullSN;
    
    public CompositeSN() {
    }
    
    public CompositeSN(String type, String tenantId, String dateString, String sn) {
        this.type = type;
        this.tenantId = tenantId;
        this.dateString = dateString;
        dateStringToDate();
        this.sn = sn;
        assembleFullSN();
    }
    
    public CompositeSN(String type, String tenantId, Date date, long sn) {
        this.type = type;
        this.tenantId = tenantId;
        this.date = date;
        dateToDateString();
        this.sn = longSNToStringSN(sn);
        assembleFullSN();
    }
    
    public CompositeSN(String fullSN) {
        this.fullSN = fullSN;
        parseFullSN();
    }
    
    public static CompositeSN fromParts(String type, String tenantId, String dateString, String sn) {
        return new CompositeSN(type, tenantId, dateString, sn);
    }
    
    public static CompositeSN fromParts(String type, String tenantId, Date date, long sn) {
        return new CompositeSN(type, tenantId, date, sn);
    }
    
    public static CompositeSN fromFullSN(String fullSN) {
        return new CompositeSN(fullSN);
    }
    
    public static CompositeSN fromPartsNoCheck(String type, String tenantId, String dateString, String sn) {
        CompositeSN cSN = new CompositeSN();
        cSN.type = type;
        cSN.tenantId = tenantId;
        cSN.dateString = dateString;
        cSN.dateStringToDate();
        cSN.sn = sn;
        return cSN;
    }
    
    public static CompositeSN fromPartsNoCheck(String type, String tenantId, Date date, long sn) {
        CompositeSN cSN = new CompositeSN();
        cSN.type = type;
        cSN.tenantId = tenantId;
        cSN.date = date;
        cSN.dateToDateString();
        cSN.sn = longSNToStringSN(sn);
        return cSN;
    }
    
    public static String assembleFullSNNoCheck(String type, String tenantId, String dateString, String sn) {
        return new StringBuilder(FULL_LEN).append(type).append(tenantId).append(dateString).append(sn).toString();
    }
    
    public static String assembleFullSNNoCheck(String type, String tenantId, Date date, long sn) {
        return new StringBuilder(FULL_LEN).append(type).append(tenantId).append(dateToDateString(date))
                .append(longSNToStringSN(sn)).toString();
    }
    
    private void parseFullSN() {
        if (StringUtils.isBlank(fullSN) || FULL_LEN != fullSN.length()) {
            throw new HSBCSErrInfoException(ValidationErrorCodes.PARAM_MALFORMED);
        }
        Matcher matcher = fullSNPattern.matcher(fullSN);
        if (matcher.find()) {
            type = matcher.group(1);
            tenantId = matcher.group(2);
            dateString = matcher.group(3);
            dateStringToDate();
            sn = matcher.group(4);
        } else {
            throw new HSBCSErrInfoException(ValidationErrorCodes.PARAM_MALFORMED);
        }
    }
    
    private void assembleFullSN() {
        fullSN = assembleFullSNNoCheck(type, tenantId, dateString, sn);
    }
    
    private void dateStringToDate() {
        try {
            date = DateTimeUtil.toDate(dateString, DATE_FORMAT);
        } catch (ParseException e) {
            throw new HSBCSErrInfoException(e, ValidationErrorCodes.PARAM_MALFORMED);
        }
    }
    
    public static String dateToDateString(Date date) {
        return DateTimeUtil.toString(date, DATE_FORMAT);
    }
    
    private void dateToDateString() {
        dateString = dateToDateString(date);
    }
    
    public static String longSNToStringSN(long longSN) {
        if (SN_LIMIT < longSN) {
            // 大于限制则抛异常
            throw new HSBCSErrInfoException(ValidationErrorCodes.PARAM_MALFORMED, "sn to big, sn=" + longSN
                    + ", define sn width: " + PART_LEN_SN);
        }
        return new DecimalFormat(SN_FORMAT).format(longSN);
    }
    
    public String getType() {
        return type;
    }
    
    public void setType(String type) {
        this.type = type;
        fullSN = null;
    }
    
    public String getTenantId() {
        return tenantId;
    }
    
    public void setTenantId(String tenantId) {
        this.tenantId = tenantId;
        fullSN = null;
    }
    
    public String getDateString() {
        return dateString;
    }
    
    public void setDateString(String dateString) {
        this.dateString = dateString;
        dateStringToDate();
        fullSN = null;
    }
    
    public Date getDate() {
        return date;
    }
    
    public void setDate(Date date) {
        this.date = date;
        dateToDateString();
        fullSN = null;
    }
    
    public String getSn() {
        return sn;
    }
    
    public void setSn(String sn) {
        this.sn = sn;
        fullSN = null;
    }
    
    public String getFullSN() {
        if (null == fullSN) {
            assembleFullSN();
        }
        return fullSN;
    }
    
    public void setFullSN(String fullSN) {
        this.fullSN = fullSN;
        parseFullSN();
    }
    
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("CompositeSN [type=").append(type).append(", tenantId=").append(tenantId)
                .append(", dateString=").append(dateString).append(", sn=").append(sn).append("]");
        return builder.toString();
    }
    
}
